#include "stdafx.h"
#include "net/HTTPResponse.hpp"
#include "net/NameValueCollection.hpp"
#include "utils/numberparser.hpp"
#include "utils/numberformatter.hpp"
using JHCPP::utils::CNumberParser;
using JHCPP::utils::CNumberFormatter;
#include "stdlib/datetime.hpp"
#include "stdlib/timestamp.hpp"
#include "stdlib/datetimeparser.hpp"
#include "stdlib/datetimeformat.hpp"
#include "stdlib/datetimeformatter.hpp"
using JHCPP::stdlib::CDateTime;
using JHCPP::stdlib::CTimeStamp;
using JHCPP::stdlib::CDateTimeParser;
using JHCPP::stdlib::CDateTimeFormat;
using JHCPP::stdlib::CDateTimeFormatter;
#include "charset/string.hpp"
#include "charset/ascii.hpp"
using JHCPP::charset::icompare;
#include "stdlib/exception.hpp"
using namespace JHCPP::stdlib::jhException;

NET_NAMESPACE_BEGIN

const std::string CHTTPResponse::HTTP_REASON_CONTINUE                        = "Continue";
const std::string CHTTPResponse::HTTP_REASON_SWITCHING_PROTOCOLS             = "Switching Protocols";
const std::string CHTTPResponse::HTTP_REASON_OK                              = "OK";
const std::string CHTTPResponse::HTTP_REASON_CREATED                         = "Created";
const std::string CHTTPResponse::HTTP_REASON_ACCEPTED                        = "Accepted";
const std::string CHTTPResponse::HTTP_REASON_NONAUTHORITATIVE                = "Non-Authoritative Information";
const std::string CHTTPResponse::HTTP_REASON_NO_CONTENT                      = "No Content";
const std::string CHTTPResponse::HTTP_REASON_RESET_CONTENT                   = "Reset Content";
const std::string CHTTPResponse::HTTP_REASON_PARTIAL_CONTENT                 = "Partial Content";
const std::string CHTTPResponse::HTTP_REASON_MULTIPLE_CHOICES                = "Multiple Choices";
const std::string CHTTPResponse::HTTP_REASON_MOVED_PERMANENTLY               = "Moved Permanently";
const std::string CHTTPResponse::HTTP_REASON_FOUND                           = "Found";
const std::string CHTTPResponse::HTTP_REASON_SEE_OTHER                       = "See Other";
const std::string CHTTPResponse::HTTP_REASON_NOT_MODIFIED                    = "Not Modified";
const std::string CHTTPResponse::HTTP_REASON_USEPROXY                        = "Use Proxy";
const std::string CHTTPResponse::HTTP_REASON_TEMPORARY_REDIRECT              = "Temporary Redirect";
const std::string CHTTPResponse::HTTP_REASON_BAD_REQUEST                     = "Bad Request";
const std::string CHTTPResponse::HTTP_REASON_UNAUTHORIZED                    = "Unauthorized";
const std::string CHTTPResponse::HTTP_REASON_PAYMENT_REQUIRED                = "Payment Required";
const std::string CHTTPResponse::HTTP_REASON_FORBIDDEN                       = "Forbidden";
const std::string CHTTPResponse::HTTP_REASON_NOT_FOUND                       = "Not Found";
const std::string CHTTPResponse::HTTP_REASON_METHOD_NOT_ALLOWED              = "Method Not Allowed";
const std::string CHTTPResponse::HTTP_REASON_NOT_ACCEPTABLE                  = "Not Acceptable";
const std::string CHTTPResponse::HTTP_REASON_PROXY_AUTHENTICATION_REQUIRED   = "Proxy Authentication Required";
const std::string CHTTPResponse::HTTP_REASON_REQUEST_TIMEOUT                 = "Request Time-out";
const std::string CHTTPResponse::HTTP_REASON_CONFLICT                        = "Conflict";
const std::string CHTTPResponse::HTTP_REASON_GONE                            = "Gone";
const std::string CHTTPResponse::HTTP_REASON_LENGTH_REQUIRED                 = "Length Required";
const std::string CHTTPResponse::HTTP_REASON_PRECONDITION_FAILED             = "Precondition Failed";
const std::string CHTTPResponse::HTTP_REASON_REQUESTENTITYTOOLARGE           = "Request Entity Too Large";
const std::string CHTTPResponse::HTTP_REASON_REQUESTURITOOLONG               = "Request-URI Too Large";
const std::string CHTTPResponse::HTTP_REASON_UNSUPPORTEDMEDIATYPE            = "Unsupported Media Type";
const std::string CHTTPResponse::HTTP_REASON_REQUESTED_RANGE_NOT_SATISFIABLE = "Requested Range Not Satisfiable";
const std::string CHTTPResponse::HTTP_REASON_EXPECTATION_FAILED              = "Expectation Failed";
const std::string CHTTPResponse::HTTP_REASON_INTERNAL_SERVER_ERROR           = "Internal Server Error";
const std::string CHTTPResponse::HTTP_REASON_NOT_IMPLEMENTED                 = "Not Implemented";
const std::string CHTTPResponse::HTTP_REASON_BAD_GATEWAY                     = "Bad Gateway";
const std::string CHTTPResponse::HTTP_REASON_SERVICE_UNAVAILABLE             = "Service Unavailable";
const std::string CHTTPResponse::HTTP_REASON_GATEWAY_TIMEOUT                 = "Gateway Time-out";
const std::string CHTTPResponse::HTTP_REASON_VERSION_NOT_SUPPORTED           = "HTTP Version not supported";
const std::string CHTTPResponse::HTTP_REASON_UNKNOWN                         = "???";
const std::string CHTTPResponse::DATE       = "Date";
const std::string CHTTPResponse::SET_COOKIE = "Set-Cookie";


CHTTPResponse::CHTTPResponse() : m_status(HTTP_OK), m_reason(getReasonForStatus(HTTP_OK))
{
}

CHTTPResponse::CHTTPResponse(HTTPStatus status, const std::string& reason) : m_status(status), m_reason(reason)
{
}

CHTTPResponse::CHTTPResponse(const std::string& version, HTTPStatus status, const std::string& reason)
	: CHTTPMessage(version), m_status(status), m_reason(reason)
{
}

CHTTPResponse::CHTTPResponse(HTTPStatus status) : m_status(status), m_reason(getReasonForStatus(status))
{
}

CHTTPResponse::CHTTPResponse(const std::string& version, HTTPStatus status)
	: CHTTPMessage(version), m_status(status), m_reason(getReasonForStatus(status))
{
}

CHTTPResponse::~CHTTPResponse()
{
}

void CHTTPResponse::setStatus(HTTPStatus status)
{
	m_status = status;
}

void CHTTPResponse::setStatus(const std::string& status)
{
	setStatus((HTTPStatus) CNumberParser::parse(status));
}

CHTTPResponse::HTTPStatus CHTTPResponse::getStatus() const
{
	return m_status;
}

void CHTTPResponse::setReason(const std::string& reason)
{
	m_reason = reason;
}

const std::string& CHTTPResponse::getReason() const
{
	return m_reason;
}

void CHTTPResponse::setStatusAndReason(HTTPStatus status, const std::string& reason)
{
	m_status = status;
	m_reason = reason;
}

void CHTTPResponse::setStatusAndReason(HTTPStatus status)
{
	setStatusAndReason(status, getReasonForStatus(status));
}

void CHTTPResponse::setDate(const CTimeStamp& dateTime)
{
	set(DATE, CDateTimeFormatter::format(dateTime, CDateTimeFormat::HTTP_FORMAT));
}

CTimeStamp CHTTPResponse::getDate() const
{
	const std::string& dateTime = get(DATE);
	int tzd;
	return CDateTimeParser::parse(dateTime, tzd).timestamp();
}

void CHTTPResponse::addCookie(const CHTTPCookie& cookie)
{
	add(SET_COOKIE, cookie.toString());
}

void CHTTPResponse::getCookies(std::vector<CHTTPCookie>& cookies) const
{
	cookies.clear();
	CNameValueCollection::ConstIterator it = find(SET_COOKIE);
	while (it != end() && icompare(it->first, SET_COOKIE) == 0)
	{
		CNameValueCollection nvc;
		splitParameters(it->second.begin(), it->second.end(), nvc);
		cookies.push_back(CHTTPCookie(nvc));
		++it;
	}
}

void CHTTPResponse::write(std::ostream& ostr) const
{
	ostr << getVersion() << " " << static_cast<int>(m_status) << " " << m_reason << "\r\n";
	CHTTPMessage::write(ostr);
	ostr << "\r\n";
}

void CHTTPResponse::read(std::istream& istr)
{
	static const int eof = std::char_traits<char>::eof();

	std::string version;
	std::string status;
	std::string reason;

	int ch =  istr.get();
	if (istr.bad()) throw NetException("Error reading HTTP response header");
	if (ch == eof) throw NoMessageException();
	while (CAscii::isSpace(ch)) ch = istr.get();
	if (ch == eof) throw MessageException("No HTTP response header");
	while (!CAscii::isSpace(ch) && ch != eof && version.length() < MAX_VERSION_LENGTH) { version += (char) ch; ch = istr.get(); }
	if (!CAscii::isSpace(ch)) throw MessageException("Invalid HTTP version string");
	while (CAscii::isSpace(ch)) ch = istr.get();
	while (!CAscii::isSpace(ch) && ch != eof && status.length() < MAX_STATUS_LENGTH) { status += (char) ch; ch = istr.get(); }
	if (!CAscii::isSpace(ch)) throw MessageException("Invalid HTTP status code");
	while (CAscii::isSpace(ch) && ch != '\r' && ch != '\n' && ch != eof) ch = istr.get();
	while (ch != '\r' && ch != '\n' && ch != eof && reason.length() < MAX_REASON_LENGTH) { reason += (char) ch; ch = istr.get(); }
	if (!CAscii::isSpace(ch)) throw MessageException("HTTP reason string too long");
	if (ch == '\r') ch = istr.get();

	CHTTPMessage::read(istr);
	ch = istr.get();
	while (ch != '\n' && ch != eof) { ch = istr.get(); }
	setVersion(version);
	setStatus(status);
	setReason(reason);
}

const std::string& CHTTPResponse::getReasonForStatus(HTTPStatus status)
{
	switch (status)
	{
	case HTTP_CONTINUE: 
		return HTTP_REASON_CONTINUE;
	case HTTP_SWITCHING_PROTOCOLS: 
		return HTTP_REASON_SWITCHING_PROTOCOLS;
	case HTTP_OK: 
		return HTTP_REASON_OK;
	case HTTP_CREATED: 
		return HTTP_REASON_CREATED;
	case HTTP_ACCEPTED: 
		return HTTP_REASON_ACCEPTED;
	case HTTP_NONAUTHORITATIVE:	
		return HTTP_REASON_NONAUTHORITATIVE;
	case HTTP_NO_CONTENT: 
		return HTTP_REASON_NO_CONTENT;
	case HTTP_RESET_CONTENT: 
		return HTTP_REASON_RESET_CONTENT;
	case HTTP_PARTIAL_CONTENT: 
		return HTTP_REASON_PARTIAL_CONTENT;
	case HTTP_MULTIPLE_CHOICES: 
		return HTTP_REASON_MULTIPLE_CHOICES;
	case HTTP_MOVED_PERMANENTLY: 
		return HTTP_REASON_MOVED_PERMANENTLY;
	case HTTP_FOUND: 
		return HTTP_REASON_FOUND;
	case HTTP_SEE_OTHER: 
		return HTTP_REASON_SEE_OTHER;
	case HTTP_NOT_MODIFIED: 
		return HTTP_REASON_NOT_MODIFIED;
	case HTTP_USEPROXY: 
		return HTTP_REASON_USEPROXY;
	case HTTP_TEMPORARY_REDIRECT: 
		return HTTP_REASON_TEMPORARY_REDIRECT;
	case HTTP_BAD_REQUEST: 
		return HTTP_REASON_BAD_REQUEST;
	case HTTP_UNAUTHORIZED: 
		return HTTP_REASON_UNAUTHORIZED;
	case HTTP_PAYMENT_REQUIRED: 
		return HTTP_REASON_PAYMENT_REQUIRED;
	case HTTP_FORBIDDEN: 
		return HTTP_REASON_FORBIDDEN;
	case HTTP_NOT_FOUND: 
		return HTTP_REASON_NOT_FOUND;
	case HTTP_METHOD_NOT_ALLOWED:
		return HTTP_REASON_METHOD_NOT_ALLOWED;
	case HTTP_NOT_ACCEPTABLE: 
		return HTTP_REASON_NOT_ACCEPTABLE;
	case HTTP_PROXY_AUTHENTICATION_REQUIRED: 
		return HTTP_REASON_PROXY_AUTHENTICATION_REQUIRED;
	case HTTP_REQUEST_TIMEOUT: 
		return HTTP_REASON_REQUEST_TIMEOUT;
	case HTTP_CONFLICT: 
		return HTTP_REASON_CONFLICT;
	case HTTP_GONE: 
		return HTTP_REASON_GONE;
	case HTTP_LENGTH_REQUIRED: 
		return HTTP_REASON_LENGTH_REQUIRED;
	case HTTP_PRECONDITION_FAILED: 
		return HTTP_REASON_PRECONDITION_FAILED;
	case HTTP_REQUESTENTITYTOOLARGE: 
		return HTTP_REASON_REQUESTENTITYTOOLARGE;
	case HTTP_REQUESTURITOOLONG: 
		return HTTP_REASON_REQUESTURITOOLONG;
	case HTTP_UNSUPPORTEDMEDIATYPE: 
		return HTTP_REASON_UNSUPPORTEDMEDIATYPE;
	case HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: 
		return HTTP_REASON_REQUESTED_RANGE_NOT_SATISFIABLE;
	case HTTP_EXPECTATION_FAILED: 
		return HTTP_REASON_EXPECTATION_FAILED;
	case HTTP_INTERNAL_SERVER_ERROR: 
		return HTTP_REASON_INTERNAL_SERVER_ERROR;
	case HTTP_NOT_IMPLEMENTED: 
		return HTTP_REASON_NOT_IMPLEMENTED;
	case HTTP_BAD_GATEWAY: 
		return HTTP_REASON_BAD_GATEWAY;
	case HTTP_SERVICE_UNAVAILABLE:
		return HTTP_REASON_SERVICE_UNAVAILABLE;
	case HTTP_GATEWAY_TIMEOUT: 
		return HTTP_REASON_GATEWAY_TIMEOUT;
	case HTTP_VERSION_NOT_SUPPORTED: 
		return HTTP_REASON_VERSION_NOT_SUPPORTED;
	default: 
		return HTTP_REASON_UNKNOWN;
	}
}


NET_NAMESPACE_END
